/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// RTP sink for AMR audio (RFC 4867)
// Implementation

// NOTE: At present, this is just a limited implementation, supporting:
// octet-alignment only; no interleaving; no frame CRC; no robust-sorting.

#include "include/AMRAudioRTPSink.hh"
#include "include/AMRAudioSource.hh"

AMRAudioRTPSink *
AMRAudioRTPSink::createNew(UsageEnvironment &env, Groupsock *RTPgs,
                           unsigned char rtpPayloadFormat,
                           Boolean sourceIsWideband,
                           unsigned numChannelsInSource) {
    return new AMRAudioRTPSink(env, RTPgs, rtpPayloadFormat,
                               sourceIsWideband, numChannelsInSource);
}

AMRAudioRTPSink
::AMRAudioRTPSink(UsageEnvironment &env, Groupsock *RTPgs,
                  unsigned char rtpPayloadFormat,
                  Boolean sourceIsWideband, unsigned numChannelsInSource)
        : AudioRTPSink(env, RTPgs, rtpPayloadFormat,
                       sourceIsWideband ? 16000 : 8000,
                       sourceIsWideband ? "AMR-WB" : "AMR",
                       numChannelsInSource),
          fSourceIsWideband(sourceIsWideband), fFmtpSDPLine(NULL) {
}

AMRAudioRTPSink::~AMRAudioRTPSink() {
    delete[] fFmtpSDPLine;
}

Boolean AMRAudioRTPSink::sourceIsCompatibleWithUs(MediaSource &source) {
    // Our source must be an AMR audio source:
    if (!source.isAMRAudioSource()) return False;

    // Also, the source must be wideband iff we asked for this:
    AMRAudioSource &amrSource = (AMRAudioSource &) source;
    if ((amrSource.isWideband() ^ fSourceIsWideband) != 0) return False;

    // Also, the source must have the same number of channels that we
    // specified.  (It could, in principle, have more, but we don't
    // support that.)
    if (amrSource.numChannels() != numChannels()) return False;

    // Also, because in our current implementation we output only one
    // frame in each RTP packet, this means that for multi-channel audio,
    // each 'frame-block' will be split over multiple RTP packets, which
    // may violate the spec.  Warn about this:
    if (amrSource.numChannels() > 1) {
        envir() << "AMRAudioRTPSink: Warning: Input source has " << amrSource.numChannels()
                << " audio channels.  In the current implementation, the multi-frame frame-block will be split over multiple RTP packets\n";
    }

    return True;
}

void AMRAudioRTPSink::doSpecialFrameHandling(unsigned fragmentationOffset,
                                             unsigned char *frameStart,
                                             unsigned numBytesInFrame,
                                             struct timeval framePresentationTime,
                                             unsigned numRemainingBytes) {
    // If this is the 1st frame in the 1st packet, set the RTP 'M' (marker)
    // bit (because this is considered the start of a talk spurt):
    if (isFirstPacket() && isFirstFrameInPacket()) {
        setMarkerBit();
    }

    // If this is the first frame in the packet, set the 1-byte payload
    // header (using CMR 15)
    if (isFirstFrameInPacket()) {
        u_int8_t payloadHeader = 0xF0;
        setSpecialHeaderBytes(&payloadHeader, 1, 0);
    }

    // Set the TOC field for the current frame, based on the "FT" and "Q"
    // values from our source:
    AMRAudioSource *amrSource = (AMRAudioSource *) fSource;
    if (amrSource == NULL) return; // sanity check

    u_int8_t toc = amrSource->lastFrameHeader();
    // Clear the "F" bit, because we're the last frame in this packet: #####
    toc &= ~0x80;
    setSpecialHeaderBytes(&toc, 1, 1 + numFramesUsedSoFar());

    // Important: Also call our base class's doSpecialFrameHandling(),
    // to set the packet's timestamp:
    MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset,
                                               frameStart, numBytesInFrame,
                                               framePresentationTime,
                                               numRemainingBytes);
}

Boolean AMRAudioRTPSink
::frameCanAppearAfterPacketStart(unsigned char const * /*frameStart*/,
                                 unsigned /*numBytesInFrame*/) const {
    // For now, pack only one AMR frame into each outgoing RTP packet: #####
    return False;
}

unsigned AMRAudioRTPSink::specialHeaderSize() const {
    // For now, because we're packing only one frame per packet,
    // there's just a 1-byte payload header, plus a 1-byte TOC #####
    return 2;
}

char const *AMRAudioRTPSink::auxSDPLine() {
    if (fFmtpSDPLine == NULL) {
        // Generate a "a=fmtp:" line with "octet-aligned=1"
        // (That is the only non-default parameter.)
        char buf[100];
        sprintf(buf, "a=fmtp:%d octet-align=1\r\n", rtpPayloadType());
        delete[] fFmtpSDPLine;
        fFmtpSDPLine = strDup(buf);
    }
    return fFmtpSDPLine;
}
